﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.ComponentModel.Composition;

namespace NuMetaheuristics
{
    /// <summary>
    /// The Fitness class is used to compare the quality of different genomes
    /// Fitness is represented with a double-precision float value. It is up 
    /// an evaluator to determine the fitness of a genome.
    /// </summary>
    /// <remarks>
    /// The Fitness class is made abstract because smaller/bigger score is not
    /// necessarily worse/better. Subclasses determine that by overriding the 
    /// CompareTo method.
    ///
    /// This represenation can be used for multi-objective optimization by 
    /// using an evaluator that accounts for all objectives.
    /// </remarks>
    /// <seealso cref="NuMetaheuristics.FitnessSmallerIsBetter"/>
    /// <seealso cref="NuMetaheuristics.FitnessBiggerIsBetter"/>
    [DataContract(Namespace = "http://numetaheuristics.codeplex.com/schemas")]
    abstract public class Fitness : IComparable<Fitness>, IEquatable<Fitness>, ICloneable
    {
        /// <summary>
        /// The Score property is used to represent fitness.
        /// </summary>
        [DataMember]
        public double Score { get; set; }

        /// <summary>
        /// The default, no-parameter constructor. Initializes the Score to 0.0.
        /// </summary>
        public Fitness()
        {
            Score = 0.0;
        }

        /// <summary>
        /// Construct a Fitness object using a given fitness score.
        /// </summary>
        public Fitness(double score)
        {
            Score = score;
        }

        /// <summary>
        /// Construct a Fitness object using a given Fitness object.
        /// </summary>
        public Fitness(Fitness other)
        {
            Score = other.Score;
        }
        
        /// <summary>
        /// Provide a string representation of the Fitness.
        /// </summary>
        /// <returns>A string produced using the Score property.</returns>
        override public string ToString()
        {
            return Score.ToString();
        }

        /// <summary>
        /// Check for equality with another Fitness object.
        /// </summary>
        /// <returns>true if the object is equal to the current object, false 
        /// otherwise.</returns>
        public bool Equals(Fitness other)
        {
            return CompareTo(other) == 0;
        }

        /// <summary>
        /// Determine if another Fitness object less than, greater than,
        /// or equal to the current object.
        /// </summary>
        /// <remarks>This method is needed in order to compare Fitness 
        /// objects where a smaller score is better. Rather than compare
        /// the scores directly, the derived class can determine if a bigger 
        /// or smaller score is better by implementing this method.
        /// </remarks>
        /// <returns>
        /// 0 if the fitness scores are equal.
        /// 1 if the current fitness score is better.
        /// -1 if the current fitness score is worse.</returns>
        abstract public int CompareTo(Fitness other);

        /// <summary>
        /// Produce an identical Fitness object with the same derived class 
        /// type.
        /// </summary>
        /// <returns>An object which is the same as the current object.</returns>
        abstract public object Clone();

        /// <summary>
        /// Overload the equal operator.
        /// </summary>
        /// <param name="a">The first Fitness object to compare.</param>
        /// <param name="b">The second Fitness object to compare.</param>
        /// <returns>true if the objects are equal, false otherwise.</returns>
        public static bool operator == (Fitness a, Fitness b)
        {
            return (a.CompareTo(b) == 0);
        }

        /// <summary>
        /// Overload the not-equal operator.
        /// </summary>
        /// <param name="a">The first Fitness object to compare.</param>
        /// <param name="b">The second Fitness object to compare.</param>
        /// <returns>true if the objects are not equal, false otherwise.</returns>
        public static bool operator != (Fitness a, Fitness b)
        {
            return (a.CompareTo(b) != 0);
        }

        /// <summary>
        /// Overload the greater-than operator.
        /// </summary>
        /// <param name="a">The first Fitness object to compare.</param>
        /// <param name="b">The second Fitness object to compare.</param>
        /// <returns>true if the first object is better, false otherwise.</returns>
        public static bool operator > (Fitness a, Fitness b)
        {
            return (a.CompareTo(b) > 0);
        }

        /// <summary>
        /// Overload the less-than operator.
        /// </summary>
        /// <param name="a">The first Fitness object to compare.</param>
        /// <param name="b">The second Fitness object to compare.</param>
        /// <returns>true if the first object is worse, false otherwise.</returns>
        public static bool operator < (Fitness a, Fitness b)
        {
            return (a.CompareTo(b) < 0);
        }

        /// <summary>
        /// Overload the greater-than-or-equal-to operator.
        /// </summary>
        /// <param name="a">The first Fitness object to compare.</param>
        /// <param name="b">The second Fitness object to compare.</param>
        /// <returns>true if the first object is better or equal, false otherwise.</returns>
        public static bool operator >= (Fitness a, Fitness b)
        {
            return (a.CompareTo(b) >= 0);
        }

        /// <summary>
        /// Overload the less-than-or-equal-to operator.
        /// </summary>
        /// <param name="a">The first Fitness object to compare.</param>
        /// <param name="b">The second Fitness object to compare.</param>
        /// <returns>true if the first object is worse or equal, false otherwise.</returns>
        public static bool operator <= (Fitness a, Fitness b)
        {
            return (a.CompareTo(b) <= 0);
        }
    }

    /// <summary>Subclass of the Fitness abstract class, where a smaller fitness 
    /// score is better.
    /// </summary>
    /// <seealso cref="NuMetaheuristics.Fitness"/>
    [DataContract(Namespace = "http://numetaheuristics.codeplex.com/schemas")]
    [Export(typeof(Fitness))]
    public class FitnessSmallerIsBetter : Fitness
    {
        /// <summary>
        /// The default, no-parameter constructor. Calls the base no-parameter 
        /// constructor, passing it the worse possible (largest), fitness 
        /// score.
        /// </summary>
        public FitnessSmallerIsBetter()
            : base(0.0)
        {
        }

        /// <summary>
        /// Construct a FitnessSmallerIsBetter object using a given fitness 
        /// score.
        /// </summary>
        public FitnessSmallerIsBetter(double score)
            : base(score)
        {
        }

        /// <summary>
        /// Construct a FitnessSmallerIsBetter object using a given 
        /// FitnessSmallerIsBetter object.
        /// </summary>
        public FitnessSmallerIsBetter(Fitness other)
            : base(other)
        {
        }
        
        /// <summary>
        /// Determine if another Fitness object less than, greater than,
        /// or equal to the current object.
        /// </summary>
        /// <returns>
        /// 0 if the fitness scores are equal.
        /// 1 if the current fitness score is better (smaller).
        /// -1 if the current fitness score is worse (bigger).</returns>
        override public int CompareTo(Fitness other)
        {
            if (Score == other.Score)
                return 0;
            else if (Score < other.Score)
                return 1;
            else
                return -1;
        }

        /// <summary>
        /// Produce an identical FitnessSmallerIsBetter object.
        /// </summary>
        /// <returns>An object which is the same as the current object.</returns>
        override public object Clone()
        {
            return new FitnessSmallerIsBetter(this);
        }
    }

    /// <summary>Subclass of the Fitness abstract class, where a bigger fitness 
    /// score is better.
    /// </summary>
    /// <seealso cref="NuMetaheuristics.Fitness"/>
    [DataContract(Namespace = "http://numetaheuristics.codeplex.com/schemas")]
    [Export(typeof(Fitness))]
    public class FitnessBiggerIsBetter : Fitness
    {
        /// <summary>
        /// The default, no-parameter constructor. Calls the base no-parameter 
        /// constructor, passing it the worse possible (smallest), non-zero 
        /// fitness score.
        /// </summary>
        public FitnessBiggerIsBetter()
            : base(0)
        {
        }

        /// <summary>
        /// Construct a FitnessBiggerIsBetter object using a given fitness 
        /// score.
        /// </summary>
        public FitnessBiggerIsBetter(double score)
            : base(score)
        {
        }

        /// <summary>
        /// Construct a FitnessBiggerIsBetter object using a given 
        /// FitnessBiggerIsBetter object.
        /// </summary>
        public FitnessBiggerIsBetter(Fitness other)
            : base(other)
        {
        }
        
        /// <summary>
        /// Determine if another Fitness object less than, greater than,
        /// or equal to the current object.
        /// </summary>
        /// <returns>
        /// 0 if the fitness scores are equal.
        /// 1 if the current fitness score is better (bigger).
        /// -1 if the current fitness score is worse (smaller).</returns>
        override public int CompareTo(Fitness other)
        {
            if (Score == other.Score)
                return 0;
            else if (Score > other.Score)
                return 1;
            else
                return -1;
        }

        /// <summary>
        /// Produce an identical FitnessBiggerIsBetter object.
        /// </summary>
        /// <returns>An object which is the same as the current object.</returns>
        override public object Clone()
        {
            return new FitnessBiggerIsBetter(this);
        }
    }
}
